﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Reflection;
using System.IO;
using System.Xml.Serialization;

namespace PublicTypes
{
    public class PluginInfo
    {
        public PluginInfo(string name, Version version, string author, string url)
        {
            m_Name = name;
            m_Version = version;
            m_Author = author;
            m_URL = url;
        }
        Version m_Version;
        string m_Name;
        string m_Author;
        string m_URL;
        public Version PluginVersion { get { return m_Version; } }
        public string PluginName { get { return m_Name; } }
        public string PluginAuthor { get { return m_Author; } }
        public string PluginURL { get { return m_URL; } }
    }

    public interface IPlugin
    {
        void PluginInit(IPluginHost host);
        void OtherPluginLoaded(IPlugin plugin);
        PluginInfo PluginInformation{ get; }
        bool Enabled { get; set; }
    }

    public interface IPluginHost
    {
        
    }

    public class PluginCollection<T> : IEnumerable where T : IPlugin
    {
        public IEnumerator GetEnumerator()
        {
            return m_LoadedPlugins.GetEnumerator();
        }
        protected List<T> m_LoadedPlugins = new List<T>();
        protected List<Type> m_LoadedTypes = new List<Type>();
        protected IPluginHost m_Host;
        public PluginCollection(IPluginHost host)
        {
            m_Host = host;
        }

        public T[] LoadedPlugins
        {
            get
            {
                return m_LoadedPlugins.ToArray();
            }
        }

        public T ModuleForType(Type t)
        {
            foreach (T p in m_LoadedPlugins)
            {
                if (p.GetType() == t)
                    return p;
            }
            return default(T);
        }

        public void LoadPlugins(Assembly asm)
        {

            Type[] types = asm.GetTypes();
            foreach (Type t in types)
            {
                try
                {
                    // check for plugin interface
                    if (typeof(T).IsAssignableFrom(t) && !t.IsAbstract)
                    {
                        LoadPlugin(t);
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(String.Format("Plugin Interface: could not load plugin {0} from {1}: {2}", t.ToString(), asm.Location, ex.Message));
                }
            }
        }
        ~PluginCollection()
        {
            foreach (T p in m_LoadedPlugins)
                SaveSettings(p);
        }

        public void LoadPlugin(Type t)
        {
            T p;
            XmlSerializer xml = new XmlSerializer(t);
            try
            {
                p = null;
                using (StreamReader s = new StreamReader(Path.Combine(Path.Combine(GlobalSettings.RuntimeSettings.SettingsDir, "Plugins"), t.Name + ".qrm")))
                {
                    p = (T)xml.Deserialize(s);
                }
            }
            catch (Exception)
            {
                // deserializing failed, create new instance
                p = (T)Activator.CreateInstance(t);
            }
            m_LoadedPlugins.Add(p);
            m_LoadedTypes.Add(t);
            if (p != null && p.Enabled)
            {
                p.PluginInit(m_Host);
                foreach (T plugin in m_LoadedPlugins)
                {
                    plugin.OtherPluginLoaded(p);
                    p.OtherPluginLoaded(plugin);
                }
            }
            System.Diagnostics.Trace.WriteLine(String.Format("Plugin Interface: Successfully loaded type {0} from plugin {1}", t.Name, Path.GetFileName(t.Assembly.Location)));
        }

        public void LoadPlugins(string path)
        {
            if (!Directory.Exists(path))
                return;
            string[] pluginFiles = Directory.GetFiles(path, "*.dll");

            foreach (string file in pluginFiles)
            {
                Assembly asm = Assembly.LoadFile(file);

                if (asm != null)
                {
                    LoadPlugins(asm);
                }
            }
        }

        public void SaveSettings(IPlugin p)
        {
            XmlSerializer xml = new XmlSerializer(p.GetType());
            DirectoryInfo d = new DirectoryInfo(Path.Combine(GlobalSettings.RuntimeSettings.SettingsDir, "Plugins"));

            try
            {
                if (!d.Exists)
                    d.Create();
                using (StreamWriter s = new StreamWriter(Path.Combine(d.FullName, p.GetType().Name + ".qrm")))
                {
                    xml.Serialize(s, p);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("Plugin error: " + ex.Message);
            }
        }
    }
}
